﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Dubbo.Rpc
{
    public class RpcInvocation<T> : IInvocation<T>
    {
        private Type[] parameterTypes;

        private object[] arguments;

        private Dictionary<string, string> attachments;

        public RpcInvocation()
        {
        }

        public RpcInvocation(IInvocation<T> invocation, IInvoker<T> invoker)
            : this(invocation.MethodName, invocation.ParameterTypes, invocation.Arguments,
                    new Dictionary<string, string>(invocation.Attachments), invocation.Invoker)
        {
            if (invoker != null)
            {
                //    URL url = invoker.getUrl();
                //    setAttachment(Constants.PATH_KEY, url.getPath());
                //    if (url.hasParameter(Constants.INTERFACE_KEY))
                //    {
                //        setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
                //    }
                //    if (url.hasParameter(Constants.GROUP_KEY))
                //    {
                //        setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
                //    }
                //    if (url.hasParameter(Constants.VERSION_KEY))
                //    {
                //        setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY, "0.0.0"));
                //    }
                //    if (url.hasParameter(Constants.TIMEOUT_KEY))
                //    {
                //        setAttachment(Constants.TIMEOUT_KEY, url.getParameter(Constants.TIMEOUT_KEY));
                //    }
                //    if (url.hasParameter(Constants.TOKEN_KEY))
                //    {
                //        setAttachment(Constants.TOKEN_KEY, url.getParameter(Constants.TOKEN_KEY));
                //    }
                //    if (url.hasParameter(Constants.APPLICATION_KEY))
                //    {
                //        setAttachment(Constants.APPLICATION_KEY, url.getParameter(Constants.APPLICATION_KEY));
                //    }
            }
        }

        public RpcInvocation(IInvocation<T> invocation)
            : this(invocation.MethodName, invocation.ParameterTypes,
                    invocation.Arguments, invocation.Attachments, invocation.Invoker)
        {
        }

        public RpcInvocation(MethodInfo method, object[] arguments)
            : this(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray(), arguments, null, null)
        {
        }

        public RpcInvocation(MethodInfo method, object[] arguments, Dictionary<string, string> attachment)
            : this(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray(), arguments, attachment, null)
        {
        }

        public RpcInvocation(string methodName, Type[] parameterTypes, object[] arguments)
            : this(methodName, parameterTypes, arguments, null, null)
        {
        }

        public RpcInvocation(string methodName, Type[] parameterTypes, object[] arguments, Dictionary<string, string> attachments)
            : this(methodName, parameterTypes, arguments, attachments, null)
        {

        }

        public RpcInvocation(string methodName, Type[] parameterTypes, object[] arguments, Dictionary<string, string> attachments, IInvoker<T> invoker)
        {
            this.MethodName = methodName;
            this.ParameterTypes = parameterTypes;
            this.Arguments = arguments;
            this.Attachments = attachments;
            this.Invoker = invoker;
        }

        public IInvoker<T> Invoker { get; set; }

        public string MethodName { get; set; }

        public Type[] ParameterTypes
        {
            get
            {
                return parameterTypes;
            }
            set
            {
                parameterTypes = value == null ? new Type[0] : value;
            }
        }

        public object[] Arguments
        {
            get
            {
                return arguments;
            }
            set
            {
                arguments = value == null ? new object[0] : value;
            }
        }

        public Dictionary<string, string> Attachments
        {
            get
            {
                return attachments;
            }
            set
            {
                attachments = value == null ? new Dictionary<string, string>() : value;
            }
        }

        public void SetAttachment(string key, string value)
        {
            if (attachments == null)
            {
                attachments = new Dictionary<string, string>();
            }
            attachments[key] = value;
        }

        public void SetAttachmentIfAbsent(string key, string value)
        {
            if (attachments == null)
            {
                attachments = new Dictionary<string, string>();
            }
            if (!attachments.ContainsKey(key))
            {
                attachments.Add(key, value);
            }
        }

        public void AddAttachments(Dictionary<string, string> attachments)
        {
            if (attachments == null)
            {
                return;
            }
            if (this.attachments == null)
            {
                this.attachments = new Dictionary<string, string>();
            }
            foreach (var kvp in attachments)
            {
                this.attachments.Add(kvp.Key, kvp.Value);
            }
        }

        public void AddAttachmentsIfAbsent(Dictionary<string, string> attachments)
        {
            if (attachments == null)
            {
                return;
            }
            foreach (var kvp in attachments)
            {
                SetAttachmentIfAbsent(kvp.Key, kvp.Value);
            }
        }

        public string GetAttachment(string key)
        {
            if (attachments == null)
            {
                return null;
            }
            return attachments[key];
        }

        public string GetAttachment(string key, string defaultValue)
        {
            if (attachments == null)
            {
                return defaultValue;
            }
            string value;
            if (!attachments.TryGetValue(key, out value))
            {
                value = defaultValue;
            }
            return value;
        }

        public override string ToString()
        {
            return "RpcInvocation [methodName=" + MethodName
                    + ", parameterTypes=" + string.Join(",", ParameterTypes.Select(pt => pt.ToString()).ToArray())
                    + ", arguments=" + string.Join(",", Arguments.Select(arg => arg.ToString()).ToArray())
                    + ", attachments=" + Attachments + "]";
        }
    }
}
